Machine Code BEEP Sequencer
J. Waterman 7-8 Dec 2020
===========================

This text file is not to my usual rigorous standards because it's something I've farted out in barely any time at all. There are probably still some bugs in it, but it does a basic (ha!) job that I need it to.

And as ever for 48K programs this has an EXTRA BONUS MICRODRIVE VERSION! And I would recommend using it as it'll save having to reload the code and number array from tape every time you BREAK and RUN, which the tape version will have to do - the microdrive will take barely a second to load 48 bytes of machine code and 885 bytes of number array.
LOAD *"m";1;"SEQUENCER" to use it. It will only work in drive 1.

--------------------------------------------------------------------------------

My voyage through the world of machine code took me to CALL 949, the ROM routine that makes the 48K Spectrum BEEP. All I knew was "put the pitch into HL, the duration into DE, and CALL 949 for the sound." It took some experimentation to find which values of HL produced which pitch, and that DE was the number of cycles, so a constant DE produces a shorter note for a higher frequency. And at the same time, I had Dave Hughes asking for some help with converting 1990s dance basslines "that sound like a party" into machine code. (Strange, the only Party I know of is the utterly malevolent organisation from George Orwell's 1984.) As a raw beginner all I could do was write the basslines in BASIC that would at least have the correct pitch and duration, and leave it to Dave's experience to convert them to machine code.

But what if I could do it myself, or at least have a program to take note inputs and spit out the correct HL and DE values for a given set of notes and tempo?

So this is what you do:

First, input the tempo in beats per minute. The default is 120, for two beats per second.

Input the note you want (C,D,E,F,G,A,B) in upper or lower case. For a sharp, put a # character after the note, and a lower case b for a flat. Then, input the duration in beats. You'll hear the note for a fixed 0.1 seconds. Lather, rinse, repeat.

Input "O" instead of a note to change the octave. It is set by default to 4, where C4 is middle C and A6 is tuned to the standard concert pitch of 440 Hz. The range of notes available - and the program will check this - is from A0 to C8, the range of a standard piano keyboard (though some cheap models only go as high as A7, like the one I had as a nipper). Attempting to input a note outside of this range will cause the input routine to throw a strop and demand you input your note again. "White key accidentials" (i.e. E#, Fb, B#, Cb) are allowed, but double-sharps and double-flats have not been implemented, and if you're looking for microtonal music, you're too weird for this program.

The screen will show the noted you've selected, as well as the BASIC BEEP pitch value and the duration in seconds (also for BEEP) if you want to note these, followed by the HL and DE values for each note in a machine code routine.

All the BASIC pitch and beats values are held in an array m(999,2), hence there is a maximum of 999 notes in the sequence. There is space for 1332 notes in the machine code block but I've only left space on screen for notes 1-999, so hard luck if you need 1000. Though if you do, you'll be using a better program. The note names with their octaves are held in an array m$, so all these can be recalled later.

Input "P" instead of a note to play back what has been sequenced via the machine code routine held at 60000. The number of notes is automatically calculated and poked into two bytes at 60034, which is then transferred to BC. The notes are held in four bytes each from 60036, i.e.
60036 = low byte of HL
60037 = high byte of HL
60037 = low byte of DE
60038 = high byte of DE
...and so on.

At this point you can input "T" to change the tempo. All the values of DE will be recalculated from the array m(*,2), then the sequence will play back again. Input "K" to add some notes on the end, and "N" to select a note number to start changing from, to correct a mistake. These two routines aren't very well checked so I suggest you get it right straight away! But to help you identify your mistakes, input "P" and, seeing as I remembered to put all these notes into a couple of large arrays in the first place, the sequence will be dumped to the printer.

There is one big bug I know of: for some reason I can't fathom, if you record (say) a sequence of ten notes, then go back and change note six and end it there, this should cut off to a sequence of six notes. But it doesn't, even though the value 6 will be poked into BC... I really have no idea here.

At the end, when you're done, you can save the machine code routine with:
SAVE "filename" CODE 60000,(36+4*nn)
where nn is the number of notes. If you've worked it out right, the variable nn will hold the correct value, but I'd work it out yourself if I were you.

Or just save the relevant bytes for the notes with:
SAVE "notes" CODE 60036,(4*nn)
with nn as the number of notes, as before.

This program is completely bog-standard but it will serve most of my needs if I ever want to play crude tunes with machine code.


ALSO EXTRA FREE: a PDF table of the note values that correspond to the entire piano keyboard is provided - detailing the pitch values for the BEEP command on the 48K Spectrum, the text for the PLAY command on the 128K Spectrum, the HL pitch values for the Z80's CALL 949 instruction (as well as "DE values for one second"), and the QL's utterly crippled implementation of sound. You can probably see that the QL's BEEP command works out pitch in the same way as HL on the Z80, but only has a range of 0-255 to play with rather than 0-65535. And it really, painfully badly shows up the QL's sound limitations when the pitch value rises above middle C.